package jmine.tec.web.servlet.listener;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import jmine.tec.web.servlet.util.ServletExecutor;
import bancosys.tec.utils.reflection.ReflectionUtils;

/**
 * @author takeshi
 */
public class LazyServletContextListener implements ServletContextListener {

    private Future<?> future;

    private volatile ServletContextListener[] listeners;

    /**
     * {@inheritDoc}
     */
    public void contextDestroyed(ServletContextEvent sce) {
        if (!future.isDone()) {
            future.cancel(true);
        }
        if (listeners != null) {
            for (ServletContextListener ls : listeners) {
                ls.contextDestroyed(sce);
            }
        }
        ServletExecutor.shutdownExecutorServices(sce.getServletContext(), true);
    }

    /**
     * {@inheritDoc}
     */
    public void contextInitialized(final ServletContextEvent sce) {
        String listenerClassNames = sce.getServletContext().getInitParameter("listenerClassNames");
        if (listenerClassNames == null) {
            throw new IllegalArgumentException();
        }

        String[] cls = listenerClassNames.split(",");
        ServletContextListener[] instances = new ServletContextListener[cls.length];
        try {
            for (int i = 0; i < cls.length; i++) {
                String string = cls[i];
                @SuppressWarnings("unchecked")
                Class<ServletContextListener> type = (Class<ServletContextListener>) ReflectionUtils.findClass(string);
                instances[i] = type.getConstructor().newInstance();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        ExecutorService service = ServletExecutor.getOrCreateExecutorService(sce.getServletContext());
        this.future = service.submit(new ContextInitializerTask(sce, instances));
        this.listeners = instances;
    }

    /**
     * @author takeshi
     */
    private final class ContextInitializerTask implements Runnable {
        private final ServletContextEvent sce;

        private final ServletContextListener[] servletContextListeners;

        /**
         * @param sce {@link ServletContextEvent}
         * @param instances {@link ServletContextListener}
         */
        private ContextInitializerTask(ServletContextEvent sce, ServletContextListener[] instances) {
            this.sce = sce;
            this.servletContextListeners = instances;
        }

        /**
         * {@inheritDoc}
         */
        public void run() {
            try {
                for (ServletContextListener listener : this.servletContextListeners) {
                    listener.contextInitialized(sce);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

}
